﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Serialization;
using WhiteFlow.Wpf.Common.Mvvm.Model;

namespace WhiteFlow.DevTrackingTool.UI.Models
{
    [Serializable]
    [XmlRoot(ElementName = "TrackingWorksheet", IsNullable = false)]
    public class TrackingWorksheet : WorksheetItem
    {
        #region Constants

        public const string CustomersPropertyName = "Customers";

        public const string ProjectsPropertyName = "Projects";

        public const string WorksheetSettingsPropertyName = "WorksheetSettings";

        #endregion

        #region Private Fields

        private bool isDirty;

        private string title;
        private ObservableCollection<TrackingDay> days;
        private ObservableCollection<Customer> customers;
        private ObservableCollection<Project> projects;

        private WorksheetSettings worksheetSettings;
        
        #endregion

        #region Public Serialized Properties

        /// <summary>
        /// Gets or sets the title of the worksheet, given by the name of the worksheet's file
        /// without the extension (*.tttws).
        /// </summary>
        public string Title
        {
            get { return title; }
            set
            {
                title = value;
                OnPropertyChanged("Title");
                OnWorksheetItemChanged();

                // Raise a fake PropertyChanged event for the property TitleToDisplayInTabHeader,
                // because when the IsDirty property changes the TitleToDisplayInTabHeader has a
                // different value too.
                OnPropertyChanged("TitleToDisplayInTabHeader");
            }
        }

        /// <summary>
        /// Gets or sets the tracked months for the current worksheet.
        /// </summary>
        [XmlArray(ElementName = "Days", IsNullable = true)]
        [XmlArrayItem(ElementName = "Day", IsNullable = false, Type = typeof(TrackingDay))]
        public ObservableCollection<TrackingDay> Days
        {
            get
            {
                if (days == null)
                    days = new ObservableCollection<TrackingDay>();

                return days;
            }
            set
            {
                days = value;
                OnPropertyChanged("Days");
                OnWorksheetItemChanged();
            }
        }

        /// <summary>
        /// Gets the customers list of the currently active worksheet.
        /// </summary>
        [XmlArray(ElementName = "Customers", IsNullable = true)]
        [XmlArrayItem(ElementName = "Customer", IsNullable = false, Type = typeof(Customer))]
        public ObservableCollection<Customer> Customers
        {
            get { return customers; }
            set
            {
                customers = value;
                OnPropertyChanged(CustomersPropertyName);
                OnWorksheetItemChanged();
            }
        }

        /// <summary>
        /// Gets all customers names from customers list.
        /// </summary>
        [XmlIgnore]
        public List<string> CustomersNames
        {
            get
            {
                if (customers == null)
                    return null;

                return customers.Select(c => c.Name).ToList();
            }
        }

        /// <summary>
        /// Gets the projects list of the currently active worksheet.
        /// </summary>
        [XmlArray(ElementName = "Projects", IsNullable = true)]
        [XmlArrayItem(ElementName = "Project", IsNullable = false, Type = typeof(Project))]
        public ObservableCollection<Project> Projects
        {
            get { return projects; }
            set
            {
                projects = value;
                OnPropertyChanged(ProjectsPropertyName);
                OnWorksheetItemChanged();
            }
        }

        /// <summary>
        /// Gets all projects names from projects list.
        /// </summary>
        [XmlIgnore]
        public List<string> ProjectsNames
        {
            get
            {
                if (projects == null)
                    return null;

                return projects.Select(p => p.Name).ToList();
            }
        }

        [XmlElement(ElementName = "WorksheetSettings", Type = typeof(WorksheetSettings), IsNullable = false)]
        public WorksheetSettings WorksheetSettings
        {
            get
            {
                if (worksheetSettings == null)
                {
                    worksheetSettings = new WorksheetSettings();
                    worksheetSettings.SetOwner(this);
                }

                return worksheetSettings;
            }
            set
            {
                worksheetSettings = value;
                OnPropertyChanged(WorksheetSettingsPropertyName);
                OnWorksheetItemChanged();
            }
        }

        #endregion

        #region Public Not-Serialized Properties

        /// <summary>
        /// Gets or sets the worksheet's physical path.
        /// </summary>
        /// <value>
        /// The physical path.
        /// </value>
        [XmlIgnore]
        public string PhysicalPath { get; set; }

        /// <summary>
        /// Gets or sets a value that specifies whether the worksheet has been modified and
        /// therefore needs to be saved.
        /// </summary>
        [XmlIgnore]
        public bool IsDirty
        {
            get { return isDirty; }
            set
            {
                isDirty = value;
                OnPropertyChanged("IsDirty");

                // Raise a fake PropertyChanged event for the property TitleToDisplayInTabHeader,
                // because when the IsDirty property changes the TitleToDisplayInTabHeader has a
                // different value too.
                OnPropertyChanged("TitleToDisplayInTabHeader");
            }
        }

        /// <summary>
        /// Gets the title to display in the header of the tab in the UI.
        /// </summary>
        /// <remarks>
        /// This value changes when the worksheet is dirty, to specify in the UI that the worksheet
        /// has been modified and needs to be saved.
        /// </remarks>
        [XmlIgnore]
        public string TitleToDisplayInTabHeader
        {
            get { return (IsDirty ? String.Format("{0} *", Title) : Title); }
        }

        /// <summary>
        /// Gets all the location included in the other tasks of the worksheet.
        /// </summary>
        [XmlIgnore]
        public List<string> AllLocations
        {
            get
            {
                List<string> allLocations = null;

                if (Days != null && Days.Count > 0)
                {
                    allLocations = new List<string>();

                    foreach (TrackingDay day in Days)
                    {
                        if (day.ExecutedTasks != null && day.ExecutedTasks.Count > 0)
                        {
                            foreach (TaskExecution taskExecution in day.ExecutedTasks)
                            {
                                string currentLocation = taskExecution.Location;

                                if (!String.IsNullOrEmpty(currentLocation) && allLocations.Find(l => l.ToUpper().Equals(currentLocation.ToUpper())) == null)
                                    allLocations.Add(currentLocation);
                            }
                        }
                    }

                    if (allLocations.Count == 0) allLocations = null;
                }

                return allLocations;
            }
        }

        /// <summary>
        /// Gets all the titles of the other tasks of the worksheet.
        /// </summary>
        [XmlIgnore]
        public List<string> AllTaskTitles
        {
            get
            {
                List<string> allTaskTitles = null;

                if (Days != null && Days.Count > 0)
                {
                    allTaskTitles = new List<string>();

                    foreach (TrackingDay day in Days)
                    {
                        if (day.ExecutedTasks != null && day.ExecutedTasks.Count > 0)
                        {
                            foreach (TaskExecution taskExecution in day.ExecutedTasks)
                            {
                                string currentTaskTitle = taskExecution.TaskTitle;

                                if (!String.IsNullOrEmpty(currentTaskTitle) && allTaskTitles.Find(l => l.ToUpper().Equals(currentTaskTitle.ToUpper())) == null)
                                    allTaskTitles.Add(currentTaskTitle);
                            }
                        }
                    }

                    if (allTaskTitles.Count == 0)
                        allTaskTitles = null;
                }

                return allTaskTitles;
            }
        }

        /// <summary>
        /// Gets all the descriptions of all expenses contained in current worksheet.
        /// </summary>
        [XmlIgnore]
        public List<string> AllExpensesDescriptions
        {
            get
            {
                List<string> allExpensesDescriptions = null;

                if (Days != null && Days.Count > 0)
                {
                    allExpensesDescriptions = new List<string>();

                    foreach (TrackingDay day in Days)
                    {
                        if (day.ExpenseReport != null && day.ExpenseReport.Expenses.Count > 0)
                        {
                            foreach (Expense expense in day.ExpenseReport.Expenses)
                            {
                                string description = expense.Description;

                                if (!String.IsNullOrEmpty(description) && allExpensesDescriptions.Find(d => d.ToUpper().Equals(description.ToUpper())) == null)
                                    allExpensesDescriptions.Add(description);
                            }
                        }
                    }

                    if (allExpensesDescriptions.Count == 0)
                        allExpensesDescriptions = null;
                }

                return allExpensesDescriptions;
            }
        }

        #endregion

        #region Public Methods

        public void UpdateOwners()
        {
            if (Days != null && Days.Count > 0)
            {
                foreach (TrackingDay day in Days)
                {
                    day.SetOwner(this);
                    day.ExpenseReport.SetOwner(this);

                    if (day.ExpenseReport != null)
                    {
                        foreach (Expense expense in day.ExpenseReport.Expenses)
                        {
                            expense.SetOwner(day.ExpenseReport);
                        }
                    }

                    foreach (TaskExecution task in day.ExecutedTasks)
                    {
                        task.SetOwner(day);
                    }
                }
            }
        }

        #endregion
    }
}